
import CanvasOperator from './canvasOperator.js';
import CanvasRenderer from './canvasRenderer.js';
import SeatCalculator from './canvasCalculator.js';
import seatmapConfig from './config.js';
export default class seatmap {

  constructor( {canvas, seatList, options}) {
    this.ctx = canvas.getContext('2d');
    this.seatList = seatList;
    this.options = this._deepMerge(seatmapConfig,options);
    this.checkOptions();
    this.operator = new CanvasOperator(canvas);
    this.renderer = new CanvasRenderer(this.ctx,this.options);
    this.calc = new SeatCalculator(this.seatList, this.options);

    // 运行时状态
    this.cellSize   = 0;
    this.panX       = 0;
    this.panY       = 0;
    this.boundaries = {};
    this.imageScreen = null;
    this.imageGou    = null;
    this._drawing    = false;
    this._rafId      = null;
    this.seatRow     = 0;
    this.seatCol     = 0;
    this.miniMapShow = false;
    this.firstClick  = true;
    this.init()
  }
  checkOptions(){
    const { SEAT_FIELDS,canvasWidth, canvasHeight,footerHeight,SEAT_STYLE,areaList } = this.options;
    
    if (!SEAT_FIELDS.ROW_ID     || typeof SEAT_FIELDS.ROW_ID !== 'string')     throw new Error(`options.SEAT_FIELDS.ROW_ID 配置不正确`);
    if (!SEAT_FIELDS.COLUMN_ID  || typeof SEAT_FIELDS.COLUMN_ID !== 'string')  throw new Error(`options.SEAT_FIELDS.COLUMN_ID 配置不正确`);
    if (!SEAT_FIELDS.STATUS_NAME|| typeof SEAT_FIELDS.STATUS_NAME !== 'string')throw new Error(`options.SEAT_FIELDS.STATUS_NAME 配置不正确`);
    if (!SEAT_FIELDS.TYPE_NAME  || typeof SEAT_FIELDS.TYPE_NAME !== 'string')  throw new Error(`options.SEAT_FIELDS.TYPE_NAME 配置不正确`);
    if (!SEAT_FIELDS.AREA_ID    || typeof SEAT_FIELDS.AREA_ID !== 'string')    throw new Error(`options.SEAT_FIELDS.AREA_ID 配置不正确`);
    if (!canvasWidth  || canvasWidth <= 0) throw new Error(`options.canvasWidth 必须大于0`);
    if (!canvasHeight || canvasHeight <= 0) throw new Error(`options.canvasHeight 必须大于0`);
    if (footerHeight < 0) throw new Error(`options.footerHeight 不能为负`);

    const validateSeatStyle = SEAT_STYLE => ['DISABLED', 'LOCKED', 'SOLD', 'AVAILABLE', 'SELECTED'].every(prop => 
      SEAT_STYLE[prop]?.fillStyle !== undefined && SEAT_STYLE[prop]?.strokeStyle !== undefined
    );
    if(validateSeatStyle(SEAT_STYLE) === false) throw new Error(`options.SEAT_STYLE 配置不正确,每个状态都需要配置 fillStyle 和 strokeStyle`); 

    areaList.every(item=>{
      if(item[SEAT_FIELDS.AREA_ID] === undefined) throw new Error(`options.areaList 中缺少 AREA_ID 字段`);
      if(item.strokeStyle === undefined) throw new Error(`options.areaList 中缺少 strokeStyle 字段（颜色）`);
    })

  }
  async init() {
    const {canvasWidth, canvasHeight,footerHeight} = this.options;

    // 加载图片
    this.imageScreen = await this.operator.loadImage('uni_modules/shuai-seatmap/static/hall.png');
    this.imageGou    = await this.operator.loadImage('uni_modules/shuai-seatmap/static/gou.png');

    // 初始布局
    this.cellSize   = this.calc.calcCellSize(canvasWidth);
    const { seatRow , seatCol } = this.calc.calcSeatRowCol();
    const { panX, panY } = this.calc.calcInitialPan(canvasWidth, canvasHeight, this.cellSize);
    this.boundaries = this.calc.getBoundaries(canvasWidth, canvasHeight, footerHeight, this.cellSize);
    this.seatRow = seatRow;
    this.seatCol = seatCol;
    this.panX = panX;
    this.panY = panY;
    this.requestDraw();
  }

/* -------------------- 座位图绘制 -------------------- */
  requestDraw() {
    if (this._drawing) return;
    this._drawing = true;
    this._rafId = this.operator.raf(() => {
      const { panX,panY, cellSize, seatRow, seatCol, seatList,imageGou,imageScreen,miniMapShow} = this;
      const { title,canvasWidth,hallHeight,areaColorMap,hallName } = this.options;
      this.renderer.clear(0,0,this.options.canvasWidth, this.options.canvasHeight);
      this.renderer.drawSeatMap(   { panX, panY, cellSize, seatRow, seatCol, seatList,areaColorMap,imageGou});
      this.renderer.drawCenterLine({ panX, panY, cellSize, seatRow, seatCol, title});
      this.renderer.drawScreen(    { panX, panY, cellSize, seatRow, seatCol, imageScreen,hallName, canvasWidth,hallHeight});
      this.renderer.drawRowLabels( { panX, panY, cellSize, seatRow, seatCol, seatArray: this.calc.seatArray });
      this.renderer.drawMiniMap(   { panX, panY, cellSize, seatRow, seatCol, seatList,canvasWidth,miniMapShow,hallHeight});
      this._drawing = false;
    });
  }

/* -------------------- 触摸事件 -------------------- */
  onTouchStart(e) {
    this._startPanX = this.panX;
    this._startPanY = this.panY;
    this._lastTouch  = e.touches[0];
    this._startTouchEvent = e;
    this._isPinching = false;

    if (e.touches.length === 2) {
      this._isPinching   = true;
      this._lastPinchDist = this.calc.calcDistance(e.touches[0], e.touches[1]);
    }
  }

  onTouchMove(e) {
    if (e.touches.length === 1 && !this._isPinching) {
      this.miniMapShow = true; // 拖拽时显示小图
      const dx = e.touches[0].x - this._lastTouch.x;
      const dy = e.touches[0].y - this._lastTouch.y;
      this.panX = this._startPanX + dx;
      this.panY = this._startPanY + dy;
      this.requestDraw();
      return;
    }

    if (e.touches.length === 2 && this._isPinching) {
      this.miniMapShow = true;
      if (!this._lastPinchDist) return;
      const dist = this.calc.calcDistance(e.touches[0], e.touches[1]);
      const centerX = (e.touches[0].x + e.touches[1].x) / 2;
      const centerY = (e.touches[0].y + e.touches[1].y) / 2;
      const scale = dist / this._lastPinchDist;
      const cellSize = this.cellSize * scale;
      this.applyZoom(cellSize, centerX, centerY);
      this._lastPinchDist = dist;
      this.requestDraw();
    }
  }

  onTouchEnd() {
    this._afterDrawTimer();
    const { targetX, targetY } = this.calc.clampOffset(this.panX, this.panY, this.boundaries);
    if (this.panX === targetX && this.panY === targetY) return;
    this._animateTo(targetX, targetY);
  }

  /* -------------------- 选座 -------------------- */
  async onSeatTap({x,y}) {
    const { panX, panY } = this;
    const { AVAILABLE, SELECTED } = this.options.SEAT_STATUS;
    const { STATUS_NAME } = this.options.SEAT_FIELDS;
    const { seatMaxWidth,maxSelectNum,topHeight,isolateSeats } = this.options;
    const { canvasX, canvasY } = this.calc.clientToCanvas({x,y,panX,panY});

    const seat = this.seatList.find(item => {
      let { points: { startX, startY, endX, endY } } = item;
      return (canvasX >= startX && canvasX <= endX && canvasY >= startY && canvasY <= endY);
    });
    if(!seat || ![AVAILABLE, SELECTED].includes(seat[STATUS_NAME])) {
      return {ok:true,msg:"不是有效座位"};
    }
    const indices = this.calc.getRelatedSeatIndices(seat)
    const adjustedNum = seat[STATUS_NAME] === SELECTED ? - indices.length : indices.length;
    if(maxSelectNum && this.chooseSeatList().length + adjustedNum > maxSelectNum) {
      return {ok:false,msg:`最多可选${maxSelectNum}个座位`}; 
    };
    const targetStatus = seat[STATUS_NAME] === SELECTED ? AVAILABLE : SELECTED;
    this.calc.updateSeatStatus(targetStatus, indices);
    
    if(this.firstClick){
      this.applyZoom(seatMaxWidth, canvasX, canvasY);
      this.firstClick = false;
    }
    this.requestDraw();

    if(seat[STATUS_NAME] === SELECTED && isolateSeats && this.calc.validateSeat(seat)){
      return { ok:false,msg:"左右两边不要留空哦" };
    }
    return {ok:true,msg:"点击座位成功"};
  }

  applyZoom(cellSize,centerX,centerY){
    const { panX,panY,cellSize:oldCellSize,seatCol } = this;
    const { canvasWidth,seatMaxWidth,seatMinWidth } = this.options;
    const zoom = this.calc.calcPinchZoom({oldCellSize,scale : cellSize/oldCellSize,centerX,centerY,panX, panY,seatCol,canvasWidth,seatMaxWidth,seatMinWidth});
    if(!zoom) return;
    this.boundaries = this.calc.getBoundaries(this.options.canvasWidth, this.options.canvasHeight, this.options.footerHeight, zoom.cellSize);
    const { targetX, targetY } = this.calc.clampOffset(zoom.panX, zoom.panY,  this.boundaries);
    this.cellSize = cellSize;
    this.panX     = targetX;
    this.panY     = targetY;
  }

  chooseSeatList(){
    const { SELECTED } = this.options.SEAT_STATUS;
    const { STATUS_NAME } = this.options.SEAT_FIELDS;
    let chooseList = this.seatList.filter(item => {
      return item[STATUS_NAME] == SELECTED;
    });
    chooseList.sort((a, b) => {
      return a.clickTime - b.clickTime;
    });
    return chooseList;
  }
  cancelSeat(seat) {
    this.calc.cancelSeat(seat);
    this.requestDraw();
  }
  /* ========== 孤座检测 ========== */
  validateIsolates() {
    if(this.options.isolateSeats === false) return 0;
    let errNo = 0;
    this.chooseSeatList().forEach(seat => {
      errNo += this.calc.validateSeat(seat);
    })
    return errNo;
  }
  
  /* -------------------- 工具 -------------------- */

  _afterDrawTimer() {
    if (this._timer) clearTimeout(this._timer);
    this._timer = setTimeout(() => {
      this.miniMapShow = false;
      this.requestDraw();
    }, this.options.miniMapShowTime);
  }

  _animateTo(tx, ty) {
    const startX = this.panX, startY = this.panY;
    const duration = 300;
    const startTime = Date.now();
    const animate = () => {
      const elapsed = Date.now() - startTime;
      const prog = Math.min(elapsed / duration, 1);
      const ease = 1 - Math.pow(1 - prog, 3);
      this.panX = startX + (tx - startX) * ease;
      this.panY = startY + (ty - startY) * ease;
      this.requestDraw();
      if (prog < 1) this.operator.raf(animate);
    };
    this.operator.raf(animate);
  }
  /* ---------- 深度合并 ---------- */
  _deepMerge(target, source) {
      // 如果目标或源不是对象，直接返回源
      if (typeof target !== 'object' || target === null || Array.isArray(target)) {
          return source;
      }
      // 遍历源对象的每个属性
      for (const key in source) {
          if (source.hasOwnProperty(key)) {
              const targetValue = target[key];
              const sourceValue = source[key];
              // 如果源属性是对象，递归合并
              if (typeof sourceValue === 'object' && sourceValue !== null && !Array.isArray(sourceValue)) {
                  if (typeof targetValue !== 'object' || targetValue === null || Array.isArray(targetValue)) {
                      target[key] = {};
                  }
                  this._deepMerge(target[key], sourceValue);
              } else {
                  // 否则，直接覆盖目标属性
                  target[key] = sourceValue;
              }
          }
      }

      return target;
  }
}
